package generation;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import ec.EvolutionState;
import ec.Individual;
import ec.Subpopulation;
import ec.multiobjective.MultiObjectiveFitness;
import ec.simple.SimpleStatistics;
import ec.util.*;

public class PasswordGenerationWithMutation  {
	public static ArrayList<Individual> evaluatedIndiv = new ArrayList<Individual>();

	private String userKeyWord = null;
	private int maxSize = 0;	
	private int nbrSample;
	private EvolutionState state;
	private int speedOfComplexification;
	private int maxGeneration;
	private InputStream is;

	/**
	 * create a Password generator based on transformation individual. The base word before the transformation is userKeyWord.
	 * The maximum size of the password is maxSize. 
	 * The nbrSample is the minimum number of individual that we give to the user to be evaluated. 
	 * The speedOfComplexification, plus 1, is the number of generation that the evolutionary algorithm will realize
		alone, before giving a sample of the best individual. 
	 * maxGeneration is the maximum number of generation that the EA will run.
	 * The InputStream 'is' is the InputStream created by reading the parameter file (used only under our android application) 
	 */
	public PasswordGenerationWithMutation(String userKeyWord, int maxSize, int nbrSample, 
			int speedOfComplexification, int maxGeneration, InputStream is) {
		this.userKeyWord = userKeyWord;
		this.maxSize = maxSize;
		this.nbrSample = nbrSample;
		this.speedOfComplexification = speedOfComplexification;
		this.maxGeneration = maxGeneration;
		this.is = is;
		state = null;
	}

	//initialize our evolutionnary algorithm, with the parameter that wass given to our PasswordGenerationWithMutation
	private void initilizeGenerator(InputStream is) {
		if(state == null) {
			ParameterDatabase paramDB = null;
			try {
				paramDB = new ParameterDatabase(is);
			}
			catch (IOException e) {
				System.out.println("Error: "+e);
			}

			ParameterDatabase child = new ParameterDatabase();
			child.addParent(paramDB);
			speedOfComplexification++;
			child.set(new Parameter(Transformation.P_OURVARIABLE+"."+Transformation.I_AUTONOMEGENERATION), ""+speedOfComplexification);
			child.set(new Parameter(Transformation.P_OURVARIABLE+"."+Transformation.P_KEYWORD), userKeyWord);
			child.set(new Parameter(Transformation.P_OURVARIABLE+"."+Transformation.I_MAXSIZE), ""+maxSize);
			child.set(new Parameter("generations"), ""+maxGeneration*speedOfComplexification);
	
			state = ec.Evolve.initialize(child, 0);
			state.startFresh();
		}
	}

	/**
	 * if our current state is null, initilize it.
	 * If not, realize speedOfComplexification+1 generation in the EA, and return at least the nbrSample best Individual.
	 */
	public ArrayList<Individual> generatePassword() {
		ArrayList<Individual> modifyKeyWord = new ArrayList<Individual>();
		if(state == null) {
			initilizeGenerator(is);
		}
		int result = EvolutionState.R_NOTDONE;
		int nbrGen = 0;
		while( result == EvolutionState.R_NOTDONE ) {
			result = state.evolve();
			nbrGen ++;
			Transformation.first = true;

			if(nbrGen%Transformation.getNumberOfAutonomeGeneration() == 0) {
				Individual[] inds = ((SimpleStatistics)(state.statistics)).getBestSoFar();
				float[] bestFitness = ((MultiObjectiveFitness)(inds[0].fitness)).getObjectives();
				float bestNormalizeFitness = normalizeFitness(bestFitness);

				evaluatedIndiv = new ArrayList<Individual>();

				Subpopulation[] subpop = state.population.subpops;
				Individual[] indiv = subpop[0].individuals;

				double marge = 0;
				while(modifyKeyWord.size() < nbrSample) {
					for(int i = 0; i < indiv.length; i++) {
						float indivFitness = normalizeFitness(((MultiObjectiveFitness)(indiv[i].fitness)).getObjectives());
						if(indivFitness >= bestNormalizeFitness-marge && indivFitness < bestNormalizeFitness-marge+bestNormalizeFitness/1000) {
							modifyKeyWord.add(indiv[i]);
						}
					}
					marge += bestNormalizeFitness/1000;
				}
				return modifyKeyWord;
			}
		}
		return new ArrayList<Individual>();
	}

	//normalization of the fitness to find the bestIndividual, based on the bestFitness of the run.
	private float normalizeFitness(float[] bestFiteness) {
		return (float) (Math.sqrt((Math.pow(bestFiteness[0], 2) + Math.pow(bestFiteness[1], 2))/2));
	}

	public int getNbrSample() {
		return nbrSample;
	}

	public void setNbrSample(int nbrSample) {
		this.nbrSample = nbrSample;
	}

	public EvolutionState getState() {
		return state;
	}

	public void setState(EvolutionState state) {
		this.state = state;
	}
}